/**
 * @author 艾尼亚尔
 * @date 2025/8/18
 * @description
 */
import axios, {type InternalAxiosRequestConfig} from 'axios';
import type {AxiosInstance, AxiosResponse} from 'axios';
import type {
    CacheItem,
    CacheConfig,
    CreateAxiosOptions,
    ExtendedAxiosRequestConfig,
    Result
} from './help.ts'
import {AxiosCanceler} from './AxiosCanceler';
import {isFunction} from './help.ts'


interface QueuedTask {
    priority: number;
    resolve: () => void;
}

class PrioritySemaphore {
    private running = 0;
    private queue: QueuedTask[] = [];
    public waitingCount = 0;

    constructor(private max: number) {
    }

    async acquire(priority = 0): Promise<void> {
        if (this.running < this.max) {
            this.running++;
            return;
        }
        this.waitingCount++;
        await new Promise<void>((res) => {
            this.queue.push({priority, resolve: res});
            this.queue.sort((a, b) => b.priority - a.priority);
        });
        this.waitingCount--;
    }

    release(): void {
        this.running--;
        const next = this.queue.shift();
        if (next) {
            this.running++;
            next.resolve();
        }
    }
}

export class AxiosService {

    private instance: AxiosInstance;
    private options: CreateAxiosOptions;
    private cache = new Map<string, CacheItem>();
    private semaphore: PrioritySemaphore;
    private cacheConfig: CacheConfig;
    private canceler = new AxiosCanceler();

    constructor(opt: CreateAxiosOptions) {
        this.options = opt;
        this.instance = axios.create(opt);
        this.cacheConfig = {
            enable: true,
            maxAge: 5 * 60 * 1000,
            maxSize: 100,
            ...opt.cacheConfig,
        };
        this.semaphore = new PrioritySemaphore(opt.maxConcurrent ?? 5);
        this.setupInterceptors();
    }

    /* ---------- 缓存 key ---------- */
    private generateCacheKey<T>(config: ExtendedAxiosRequestConfig<T>): string {
        const {url, method, params, data} = config;
        return `${method || 'GET'}-${url || ''}-${JSON.stringify(params || {})}-${JSON.stringify(data || {})}`;
    }

    /* 读缓存 */
    private getCache<T>(key: string): CacheItem<Result<T>> | undefined {
        const item = this.cache.get(key) as CacheItem<Result<T>> | undefined;
        if (!item) return undefined;
        const expired = Date.now() - item.timestamp > this.cacheConfig.maxAge;
        if (expired) {
            this.cache.delete(key);
            return undefined;
        }
        return item;
    }

    /* 写缓存 */
    private setCache<T>(key: string, data: Result<T>): void {
        if (this.cache.size >= this.cacheConfig.maxSize) {
            const first = this.cache.keys().next().value as string;
            this.cache.delete(first);
        }
        this.cache.set(key, {data, timestamp: Date.now()});
    }

    //拦截器
    private setupInterceptors(): void {

        const {transform} = this.options;
        // 请求
        this.instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
            let cfg = config as ExtendedAxiosRequestConfig;
            if (transform?.beforeRequestHook && isFunction(transform.beforeRequestHook)) {
                cfg = transform.beforeRequestHook(cfg, this.options);
            }
            if (transform?.requestInterceptors && isFunction(transform.requestInterceptors)) {
                cfg = transform.requestInterceptors(cfg, this.options);
            }
            if (cfg) this.canceler.addPending(cfg);

            return cfg as InternalAxiosRequestConfig;
        })
        // 响应
        this.instance.interceptors.response.use((res) => {
            if (res.config) this.canceler.removePending(res.config);
            if (transform?.responseInterceptors && isFunction(transform.responseInterceptors)) {
                res = transform.responseInterceptors(res);
            }
            if (transform?.transformResponseData && isFunction(transform.transformResponseData)) {
                res.data = transform.transformResponseData(res, this.options);
            }
            return res;
        }, (error) => {
            if (error.config) this.canceler.removePending(error.config);
            if (transform?.responseInterceptorsCatch && isFunction(transform.responseInterceptorsCatch)) {
                transform.responseInterceptorsCatch(error);
            }
            return Promise.reject(error);
        })
    }

    /* 带重试的请求 */
    private async requestWithRetry<T>(config: ExtendedAxiosRequestConfig<T>): Promise<AxiosResponse<Result<T>>> {
        let lastError: Error | undefined;
        const retry = config.retry ?? this.options.retry ?? 0;
        const delay = config.retryDelay ?? this.options.retryDelay ?? 1000;
        const codes = config.retryableStatus ?? this.options.retryableStatus ?? [];
        for (let i = 0; i <= retry; i++) {
            try {
                return await this.instance.request<Result<T>>(config);
            } catch (error) {
                lastError = error as Error;
                const status = (error as { response?: { status: number } })?.response?.status;
                if (!status || !codes.includes(status)) throw lastError;
                if (i < retry) await new Promise((r) => setTimeout(r, delay));
            }
        }
        throw lastError;
    }

    public async request<T = unknown>(config: ExtendedAxiosRequestConfig<T>): Promise<Result<T>> {
        const priority = config.priority ?? 0;
        const useCache = config.useCache ?? this.options.useCache ?? false;
        await this.semaphore.acquire(priority);
        try {
            const key = this.generateCacheKey(config);
            if (useCache) {
                const cached = this.getCache<T>(key);
                if (cached) return cached.data;
            }
            const res = await this.requestWithRetry(config);

            if (useCache) this.setCache(key, res.data);

            return res.data;
        } finally {
            this.semaphore.release();
        }
    }
}
